package com.astersoft.lib.util;

/*
 * Copyright 2012 AsterSoft personal developer - Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 * or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * Class that provide generic method for
 * Bean class serialization on file.
 * 
 * This class can be access in static way and provide stdout log capabilities
 * 
 * @author dario.durzo
 * @version 0.1 - 2009.08.26
 */
public class ASBeanUtils {
	public static boolean logEnabled = true; 

	/**
	 * Write Object to file
	 * 
	 * @param obj - the object to serialize
	 * @param cl - the class type
	 * @param file - the output file
	 * @throws Exception - if file has problem
	 */
	public static <T> void writeBean(T obj, File file)
			throws Exception {
		if(!file.exists())
			throw new Exception("File does not exist!");

		try {
			FileOutputStream fout = new FileOutputStream(file);
			ObjectOutputStream oos = new ObjectOutputStream(fout);
			oos.writeObject(obj.getClass().cast(obj));
			oos.close();
		} catch (Exception e) { 
			throw new Exception("Error on writing file");
		}
	}

	/**
	 * Write Object to file
	 * 
	 * @param obj - the object to serialize
	 * @param cl - the class type
	 * @param pathToFile - the path to file
	 * @throws Exception - if file has problem
	 */
	public static <T> void writeBean(T obj, String pathToFile) 
			throws Exception{
		if(pathToFile == null) 
			throw new Exception("file not valid");
		File file = new File(pathToFile);

		writeBean(obj, file);
	}

	/**
	 * Read a generic bean from file
	 * 
	 * @param cl - the class type of the bean
	 * @param pathToFile - the path to the file
	 * @return - an object that represent the bean
	 * @throws Exception - if file has problem
	 */
	public static <T> T readBean(Class<T> cl, String pathToFile) throws Exception{
		if(pathToFile == null) 
			throw new Exception("file not valid");
		File file = new File(pathToFile);

		return readBean(cl, file);
	}

	/**
	 * Read a generic bean from file
	 * 
	 * @param cl - the class type of the bean
	 * @param file - the source file
	 * @return - an object that represent the bean
	 * @throws Exception - if file has problem
	 */
	public static <T> T readBean(Class<T> cl, File file) throws Exception{
		if(!file.exists()) 
			throw new Exception("file does not exist!");

		T temp = cl.newInstance();

		try {
			FileInputStream fin = new FileInputStream(file);
			ObjectInputStream ois = new ObjectInputStream(fin);
			temp = cl.cast(ois.readObject());
			ois.close();
		} catch (Exception e) { 
			throw new Exception("Error in object reading");
		}
		return temp;
	}

	/**
	 * Generic toString method that print
	 * all the variables that have a get Method related 
	 * without parameters.
	 * 
	 * @usage
	 * <pre>
	 *  BeanUtils bs = new BeanUtils();
	 *  System.out.println(bs.toString( be, TestBean.class ));
	 * </pre>
	 * 
	 * @param obj the class that call the method
	 * @param cl the type of the class
	 * @return a string representation of the class
	 */
	public static <T> String toString(T obj){
		Method[] d = obj.getClass().getMethods();
		StringBuffer buffer = new StringBuffer();

		buffer.append( "Generic toString() for Class: " + obj.getClass().getName() + "\n" );
		for(int i = 0; i < d.length; i++){
			if( d[i].getName().toLowerCase().startsWith("get") ){
				if( d[i].getParameterTypes().length == 0 ){
					String variable = d[i].getName().substring( 3, d[i].getName().length() );
					try {
						buffer.append( variable + " = " + d[i].invoke(obj.getClass().cast( obj ), new Object[]{} ) + ";\n");
					} catch ( IllegalArgumentException e ) {
						e.printStackTrace();
					} catch ( IllegalAccessException e ) {
						e.printStackTrace();
					} catch ( InvocationTargetException e ) {
						e.printStackTrace();
					}
				}
			}
		}

		return buffer.toString();
	}

	/**
	 * 
	 * @param obj
	 * @param cl
	 * @param allFields
	 * @return
	 */
	public static <T> String toString(T obj, boolean allFields) {
		Field[] fields = obj.getClass().getDeclaredFields();
		StringBuffer buffer = new StringBuffer();
		String fieldName = null;

		buffer.append("Generic allFields toString() for Class: " + obj.getClass().getName() + "\n");
		for (Field f : fields) {
			if (!"serialVersionUID".equals(f.getName())) {
				f.setAccessible(true);
				fieldName = f.getName();
				Class<?> fieldType = f.getType();
				int mod = 0;
				try	{
					mod = f.getModifiers();

					buffer.append(fieldName + " (" + Modifier.toString(mod) + " " + fieldType.getSimpleName() + ") = " + f.get(obj) + ";\n");
				} catch (Exception e) {
					buffer.append(fieldName + " (" + Modifier.toString(mod) + " " + fieldType.getSimpleName() + ") = #UNABLE TO RETRIEVE#;\n");
				}
			}
		}
		return buffer.toString();
	}
	
	/**
	 * 
	 * @param obj
	 * @param cl
	 * @param allFields
	 * @return
	 */
	public static <T> String fullToString(T obj) {
		return fullToString("", obj);
	}
	
	private static <T> String fullToString(String prefix, T obj) {
		Field[] fields = obj.getClass().getDeclaredFields();
		StringBuffer buffer = new StringBuffer();
		String fieldName = null;

		buffer.append(prefix + "Generic full toString() for Class: " + obj.getClass().getName() + "\n");
		for (Field f : fields) {
			if (!"serialVersionUID".equals(f.getName())) {
				f.setAccessible(true);
				fieldName = f.getName();
				Class<?> fieldType = f.getType();
				int mod = 0;
				if(isJreClass(fieldType)) {
					try	{
						mod = f.getModifiers();
	
						buffer.append(prefix + fieldName + " (" + Modifier.toString(mod) + " " + fieldType.getSimpleName() + ") = " + f.get(obj) + ";\n");
					} catch (Exception e) {
						buffer.append(prefix + fieldName + " (" + Modifier.toString(mod) + " " + fieldType.getSimpleName() + ") = #UNABLE TO RETRIEVE#;\n");
					}
				} else {
					try {
						buffer.append(fullToString(prefix + "\t", f.get(obj)));
					} catch (Exception e) {
						buffer.append(prefix + fieldName + " (" + Modifier.toString(mod) + " " + fieldType.getSimpleName() + ") = #UNABLE TO RETRIEVE#;\n");
					}
				}
			}
		}
		return buffer.toString();
	}
	
	private static boolean isJreClass(Class<?> clazz) {
	    ClassLoader cl = clazz.getClassLoader();
	    if (cl == null || cl.getParent() == null) {
	        return true;
	    }
	    String pkg = clazz.getPackage().getName();
	    return pkg.startsWith("java.") || pkg.startsWith("com.sun") || pkg.startsWith("sun."); 
	}
}
